#pragma once
#include <sys/select.h>
#include "socket_server.h"

#define default_port 8888

class Select
{
public:
    Select(uint16_t port = default_port)
        : _tcp_socket(make_unique<Tcp>(port)), _port(port)
    {
    }

    void Init()
    {
        _tcp_socket->Creat_socket();
        _tcp_socket->Bind();
        _tcp_socket->Listen();
        // 将需要进行select的监听套接字直接添加进来
        _fds.push_back(_tcp_socket->Get_sockfd());
    }

    void Run()
    {
        while (true)
        {
            fd_set rfds;
            FD_ZERO(&rfds); // 初始化读文件描述符集
            int max_fd = 0; // 用于标识最大文件描述符的值
            for (const auto &fd : _fds)
            {
                if (fd != -1) // 会存在有关闭的文件描述符
                {
                    FD_SET(fd, &rfds);
                    max_fd = max(max_fd, (int)fd);
                }
            }

            timeval timeout({5, 0});
            int n = select(max_fd + 1, &rfds, nullptr, nullptr, &timeout); // 负责等待多个文件描述符

            switch (n)
            {
            case 0:
                cout << "select timeout, retrying..." << endl;
                break;

            case -1:
                cerr << "select erro..." << endl;
                break;

            default: // n>0(n个文件描述符状态发生变化)
                cout << "select success, residue time:" << timeout.tv_sec << '.' << timeout.tv_usec << 's' << endl;
                Handler_select(rfds);
                break;
            }
        }
    }

    ~Select()
    {
    }

private:
    unique_ptr<Tcp> _tcp_socket;
    uint16_t _port;
    vector<uint16_t> _fds;

private:
    void Handler_select(fd_set &rfds)
    {
        for (auto &fd : _fds)
        {
            if (fd == -1)
                continue; // 连接已被关闭

            if (FD_ISSET(fd, &rfds)) // 读事件就绪
            {
                if (fd == _tcp_socket->Get_sockfd()) // 1.获取新的连接
                {
                    uint16_t connect_sockfd = _tcp_socket->Accept();
                    cout << "get a newsockfd: " << connect_sockfd << endl;
                    _fds.push_back(connect_sockfd);
                }
                else // 2.收到数据
                {
                    string buffer;
                    bool ret = _tcp_socket->Recv(fd, buffer, 1024);
                    if (!ret)
                    {
                        cerr << "读取失败..." << endl;
                        close(fd);
                        fd = -1; // 关闭的文件描述符置为-1
                    }
                    else
                    {
                        cout << "client say: " << buffer << endl;
                        buffer.clear();
                    }
                }
            }
        }
    }
};